package uy.org.softtech.cssuite.repositorios.impl;

import static org.hibernate.criterion.Order.asc;
import static org.hibernate.criterion.Projections.countDistinct;
import static org.hibernate.criterion.Restrictions.eq;
import static org.springframework.util.Assert.notNull;
import static uy.org.softtech.cssuite.entidad.Entidad.CANTIDAD_REGISTROS;
import static uy.org.softtech.cssuite.entidad.sistema.Tarea.Estado.ACTIVA;

import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

import org.apache.log4j.Logger;
import org.hibernate.Criteria;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Repository;

import uy.org.softtech.cssuite.entidad.sistema.Tarea;
import uy.org.softtech.cssuite.excepciones.EntidadNoActualizadaException;
import uy.org.softtech.cssuite.excepciones.EntidadNoEncontradaException;
import uy.org.softtech.cssuite.repositorios.RepositorioTareas;

/**
 * Clase que implementa {@link RepositorioTareas}.
 * 
 * @author fabian.lobo
 * @see RepositorioTareas
 */
@Repository(value = "repositorio-tareas")
public class RepositorioTareasImpl extends HibernateDaoSupport implements RepositorioTareas {
	
	private Logger log = Logger.getLogger(getClass());
	
	@Required
	@Resource(name = "fabrica-sesiones")
	public void fabricaSesiones(SessionFactory sessionFactory) {
		setSessionFactory(sessionFactory);
	}
	
	/**
	 * Método para verificar que todos los parámetros han sido seteados correctamente.
	 * 
	 * @author fabian.lobo
	 */
	@PostConstruct
	public void inicializacion(){
		notNull(getSessionFactory(), "La propiedad [sessionFactory] de [" + getClass().getName() + "] no ha sido establecida.");
		
		log.info("Implementación de los accesos a datos relacionados con las tareas programadas ha sido inicializado.");
	}

	@Override
	public Tarea buscarPorInstancia(String nombreInstancia) throws EntidadNoEncontradaException {
		try {
			Criteria criteria = getSession().createCriteria(Tarea.class);
			criteria.add(eq("instancia", nombreInstancia));
			
			Tarea tarea = (Tarea) criteria.uniqueResult();
			if (tarea == null) {
				throw new EntidadNoEncontradaException("No se encuentra un registro para la instancia dada [" + nombreInstancia + "].");
			}
			return tarea;
		} catch (Throwable e) {
			log.error(e);
			throw new EntidadNoEncontradaException("No se encuentra un registro para la instancia dada [" + nombreInstancia + "].");
		}
	}

	@Override
	public Tarea actualizar(Tarea tarea, String servicio) throws EntidadNoActualizadaException {
		try {
			getHibernateTemplate().merge(tarea);
		} catch (Throwable e) {
			log.error(e);
			throw new EntidadNoActualizadaException(tarea);
		}
		return tarea;
	}

	@Override
	public Tarea buscarTarea(Long id) throws EntidadNoEncontradaException {
		try {
			Tarea tarea = getHibernateTemplate().get(Tarea.class, id);
			if (tarea == null) {
				throw new EntidadNoEncontradaException("No se encuentra un registro para el ID dado [" + id + "].");
			}
			return tarea;
		} catch (Throwable e) {
			log.error(e);
			throw new EntidadNoEncontradaException(id);
		}
	}

	@Override
	public Integer tareasEnEjecucion() {
		try {
			Criteria criteria = getSession().createCriteria(Tarea.class);
			criteria.add(eq("estado", ACTIVA));
			Long total = (Long) criteria.setProjection(countDistinct("id")).uniqueResult();
			if(total > 0) {
				return Integer.valueOf(total.intValue());
			}
		} catch (Throwable e) {
			log.error(e);
		}
		return Integer.valueOf(0);
	}

	@Override
	@SuppressWarnings("unchecked")
	public Set<Tarea> listarTareas() {
		Set<Tarea> tareas = new LinkedHashSet<Tarea>();
		try {
			Criteria criteria = getSession().createCriteria(Tarea.class);
			criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
			
			//Máxima cantidad de registros para optimizar performance
			criteria.setMaxResults(CANTIDAD_REGISTROS);
			
			//Se ordena por ID en orden ascendente
			criteria.addOrder(asc("id"));
			
			List<Tarea> lista = criteria.list();
			if(!lista.isEmpty()) {
				tareas.addAll(lista);
			}
		} catch (Throwable e) {
			log.error(e);
		}
		return tareas;
	}

}
